#include "AssetManagment.h"
#include "EngineStuff.h"
#include <iostream>
#include <fstream>

AssetManagment::AssetManagment(void)
{
	m_factory = new MeshFactory();
}

AssetManagment::~AssetManagment(void)
{
	delete m_factory;
}

BitmapFileData* AssetManagment::LoadBitmapData(const char* fileName)
{
	FILE* filePtr;
	int error;
	unsigned int count;
	BITMAPFILEHEADER bitmapFileHeader;
	BITMAPINFOHEADER bitmapInfoHeader;
	int imageSize, i, j, k, index;
	unsigned char* bitmapImage;
	unsigned char height;
	BitmapFileData* image;

	// Open the height map file in binary.
	error = fopen_s(&filePtr, fileName, "rb");
	if(error != 0)
	{
		return NULL;
	}

	// Read in the file header.
	count = fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);
	if(count != 1)
	{
		return NULL;
	}

	// Read in the bitmap info header.
	count = fread(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);
	if(count != 1)
	{
		return NULL;
	}

	// Save the dimensions of the terrain.
	int m_terrainWidth = bitmapInfoHeader.biWidth;
	int m_terrainHeight = bitmapInfoHeader.biHeight;

	// Calculate the size of the bitmap image data.
	imageSize = m_terrainWidth * m_terrainHeight * 3;

	// Allocate memory for the bitmap image data.
	bitmapImage = new unsigned char[imageSize];
	if(!bitmapImage)
	{
		return NULL;
	}

	// Move to the beginning of the bitmap data.
	fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);

	// Read in the bitmap image data.
	count = fread(bitmapImage, 1, imageSize, filePtr);
	if(count != imageSize)
	{
		return NULL;
	}

	// Close the file.
	error = fclose(filePtr);
	if(error != 0)
	{
		return NULL;
	}

	// Create the structure to hold the height map data.
	image = new BitmapFileData();
	image->data = new XMFLOAT3[m_terrainWidth * m_terrainHeight];
	image->height = m_terrainHeight;
	image->width = m_terrainWidth;

	// Initialize the position in the image data buffer.
	k=0;

	// Read the image data into the height map.
	for(j=0; j<m_terrainHeight; j++)
	{
		for(i=0; i<m_terrainWidth; i++)
		{
			height = bitmapImage[k];
			
			index = (m_terrainHeight * j) + i;

			image->data[index].x = (float)i;
			image->data[index].y = (float)height;
			image->data[index].z = (float)j;

			k+=3;
		}
	}

	// Release the bitmap image data.
	delete [] bitmapImage;
	bitmapImage = 0;

	return image;
}

IMesh* AssetManagment::LoadAssimpSingleModel(const char* scenePath,bool cacheLoadedModel,bool KeepModelDateInRam  )
{
	return m_factory->LoadAssimpSingleModel(scenePath,cacheLoadedModel,KeepModelDateInRam);
}

IMesh* AssetManagment::LoadModel(const char *scenePath, const char *meshName, MeshTypeData meshType , unsigned char ModelType , bool cacheLoadedModel )
{
	return m_factory->LoadModel(scenePath, meshName, meshType , ModelType ,cacheLoadedModel);
}

SceneElements* AssetManagment::LoadScene(const char* scenePath ,SceneType stype ,bool cacheLoadedScene,bool KeepModelDateInRam  )
{
	return m_factory->LoadScene(scenePath,stype,cacheLoadedScene,KeepModelDateInRam  );
}

///EX: fxc.exe /T vs_4_0 /Fo teste.fxo effect.fx /E VS
char* AssetManagment::LoadCompiledShader(const char* FileName ,SIZE_T* size,bool cacheLoadedShader , bool CanLookInTheCache )
{
	assert(size);
	assert(FileName);	
	ifstream is(FileName); 
	is.seekg(0,ios_base::end); 
	streampos pos = is.tellg(); 
	is.seekg(0,ios_base::beg); 
	char * effectBuffer = new char[pos]; 
	is.read(effectBuffer,pos); 
	*size = pos;     
	return effectBuffer;	
}

//--------------------------------------------------------------------------------------
// Helper for compiling shaders with D3DX11
//--------------------------------------------------------------------------------------
HRESULT AssetManagment::CompileShaderFromFile( const char* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut, bool cacheLoadedShader , bool CanLookInTheCache)
{
    HRESULT hr = S_OK;

    DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined( DEBUG ) || defined( _DEBUG )
    // Set the D3DCOMPILE_DEBUG flag to embed debug information in the shaders.
    // Setting this flag improves the shader debugging experience, but still allows 
    // the shaders to be optimized and to run exactly the way they will run in 
    // the release configuration of this program.
    dwShaderFlags |= D3DCOMPILE_DEBUG;
#endif

    ID3DBlob* pErrorBlob;
    hr = D3DX11CompileFromFile( szFileName, NULL, NULL, szEntryPoint, szShaderModel, 
        dwShaderFlags, 0, NULL, ppBlobOut, &pErrorBlob, NULL );
    if( FAILED(hr) )
    {
        if( pErrorBlob != NULL )
            OutputDebugStringA( (char*)pErrorBlob->GetBufferPointer() );
        if( pErrorBlob ) pErrorBlob->Release();
        return hr;
    }
    if( pErrorBlob ) pErrorBlob->Release();

    return S_OK;
}


ID3D11ShaderResourceView* AssetManagment::LoadTexture(const char* textureName)
{
		// Load the Texture
		if(textureName!=NULL)
		{

			ID3D11ShaderResourceView* tex;
			HRESULT hr = D3DX11CreateShaderResourceViewFromFile( EngineStuff::EGraphicsDevice, textureName, NULL, NULL, &tex, NULL );
			if(FAILED(hr))
			{			
#ifdef _DEBUG
				string mes(" : NOT FOUND");					
				string name(textureName);
				name += mes;
				MessageBox(NULL,name.c_str(),"ERROR",MB_OK );
#endif
				return NULL;	
			}		
			return tex;
		}
		return NULL;
}

ID3D11ShaderResourceView * AssetManagment::RandomTexture1D(int textureWidth)
{
	HRESULT result;

	XMFLOAT4 randomValues[1024];

    for(int i = 0; i < 1024; ++i)
    {
        randomValues[i].x = RandF(-1.0f, 1.0f);
        randomValues[i].y = RandF(-1.0f, 1.0f);
        randomValues[i].z = RandF(-1.0f, 1.0f);
        randomValues[i].w = RandF(-1.0f, 1.0f);
    }

    D3D11_SUBRESOURCE_DATA initData;
    initData.pSysMem = randomValues;
	initData.SysMemPitch = 1024*sizeof(XMFLOAT4);
    initData.SysMemSlicePitch = 1024*sizeof(XMFLOAT4);
    
	//
    // Create the texture.
    //
    D3D11_TEXTURE1D_DESC texDesc;
    texDesc.Width = textureWidth;
    texDesc.MipLevels = 1;
    texDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
    texDesc.Usage = D3D11_USAGE_DEFAULT;
    texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
    texDesc.CPUAccessFlags = 0;
    texDesc.MiscFlags = 0;
    texDesc.ArraySize = 1;

    ID3D11Texture1D* randomTex = 0;    

	result = EngineStuff::EGraphicsDevice->CreateTexture1D(&texDesc,&initData, &randomTex);
	CheckErrorNULL(result);	

    //
    // Create the resource view.
    //
    D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc;
    viewDesc.Format = texDesc.Format;
    viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D;
    viewDesc.Texture1D.MipLevels = texDesc.MipLevels;
    viewDesc.Texture1D.MostDetailedMip = 0;

	ID3D11ShaderResourceView * mRandomTexRV;
	result = EngineStuff::EGraphicsDevice->CreateShaderResourceView(randomTex,&viewDesc, &mRandomTexRV);
	CheckErrorNULL(result);	

	randomTex->Release();

	return mRandomTexRV;

}

ID3D11ShaderResourceView* AssetManagment::ColorTexture(int textureWidth, int textureHeight, float r, float g, float b,float a )
{
	float rmax = max(0.0, min(1.0, r));
	int RC = floor(rmax == 1.0 ? 255 : rmax * 256.0);

	float gmax = max(0.0, min(1.0, g));
	int GC = floor(gmax == 1.0 ? 255 : gmax * 256.0);

	float bmax = max(0.0, min(1.0, b));
	int BC = floor(bmax == 1.0 ? 255 : bmax * 256.0);

	float amax = max(0.0, min(1.0, a));
	int AC = floor(amax == 1.0 ? 255 : amax * 256.0);

	//int color = 0xRRGGBBAA;
	int color = 0;		
	
	color  = (RC ) ;
	color |= (GC << 8);
	color |= (BC << 16);
	color |= AC << 24;	

#ifdef _DEBUG
	char dest[50];
	sprintf(dest,"%X",color);	
#endif

	ID3D11Texture2D* Tex;
	D3D11_TEXTURE2D_DESC textureDesc;
	HRESULT result;	

	// Initialize the render target texture description.
	ZeroMemory(&textureDesc, sizeof(textureDesc));

	// Setup the render target texture description.
	textureDesc.Width = textureWidth;
	textureDesc.Height = textureHeight;
	textureDesc.MipLevels = 1;
	textureDesc.ArraySize = 1;
	textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	textureDesc.SampleDesc.Count = 1;
	textureDesc.Usage = D3D11_USAGE_DYNAMIC;
	textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	textureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	textureDesc.SampleDesc.Count = 1;
	textureDesc.SampleDesc.Quality = 0;	

	int* data = (int*) malloc(sizeof(int) * textureWidth * textureHeight);
	for( UINT row = 0; row < textureDesc.Height; ++row )
	{		
		for( UINT col = 0; col <  textureDesc.Width; ++col )
		{
			data[col + row * textureDesc.Height] = color;
		}
	}

	D3D11_SUBRESOURCE_DATA res;
	res.SysMemPitch = textureDesc.Height * sizeof(int) ;
	res.SysMemSlicePitch = 0;
	res.pSysMem = data;

	// Create texture.
	result = EngineStuff::EGraphicsDevice->CreateTexture2D(&textureDesc,&res, &Tex);
	CheckErrorNULL(result);	
	

	D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
	srvDesc.Format = textureDesc.Format;
    srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
    srvDesc.Texture2D.MipLevels = textureDesc.MipLevels;
    srvDesc.Texture2D.MostDetailedMip = textureDesc.MipLevels -1;

	ID3D11ShaderResourceView *pSRView = NULL;

	result = EngineStuff::EGraphicsDevice->CreateShaderResourceView( Tex, &srvDesc, &pSRView );
	CheckErrorNULL(result) ;
	
	return pSRView;

}